package authentication

import (
	"encoding/json"
	log "github.com/cihub/seelog"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"tunnel/common/config"
)

//
// Connection
// @Description:
//
type Connection struct {
	UUID   string
	Config config.Config
	Conn   *net.TCPConn
}

//
// Disconnect
// @Description:
// @receiver c
//
func (c *Connection) Disconnect() {
	if c.Conn != nil {
		_ = c.Conn.Close()
	}
}

//
// AuthServerV2
// @Description:
//
type AuthServerV2 struct {
	Config  config.Auth
	online  map[string]*Connection
	handler AuthServerHandler
	lock    sync.Mutex
}

//
// NewServerV2
// @Description:
// @return *AuthServer
//
func NewServerV2(cfg config.Auth, handler AuthServerHandler) *AuthServerV2 {
	return &AuthServerV2{
		Config:  cfg,
		online:  make(map[string]*Connection),
		handler: handler,
	}
}

//
// Start
// @Description:
// @receiver s
//
func (s *AuthServerV2) Start() {
	address := strings.Join([]string{s.Config.Address, strconv.Itoa(s.Config.Port)}, ":")
	log.Info("authentication server listen on : ", address)
	addr, err := net.ResolveTCPAddr("tcp4", address)
	if err != nil {
		_ = log.Error("start auth server failed : ", err.Error())
		return
	}
	listener, err := net.ListenTCP("tcp", addr)
	defer func() {
		_ = listener.Close()
	}()
	if err != nil {
		return
	}
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			continue
		}
		go s.HandleClientConnect(conn)
	}
}

//
// Check
// @Description:
// @receiver s
// @param address
//
func (s *AuthServerV2) Check(address string) bool {
	_, ok := s.online[address]
	return ok
}

//
// HandleClientConnect
// @Description:
// @receiver s
// @param conn
//
func (s *AuthServerV2) HandleClientConnect(conn *net.TCPConn) {
	defer func() {
		if conn != nil {
			_ = conn.Close()
		}
	}()
	address := GetRemoteAddr(conn)
	buffer := make([]byte, MaxAuthPacketSize)
	for {
		n, err := conn.Read(buffer)
		if err != nil || n == 0 {
			s.handler.Disconnect(address, err)
			s.clearByAddress(address)
			return
		}
		//组合数据包
		packet := NewTransportPacket()
		err = packet.Decode(buffer[:n])
		if err != nil {
			continue
		}
		if packet.UUID != "" && packet.Type != PacketTypeUnknown {
			switch packet.Type {
			case PacketTypeLogin:
				s.login(conn, packet, address)
			case PacketTypeLogout:
				s.logout(conn, packet, address)
			case PacketTypeMsg:
				s.handler.OnMessage(address, conn, packet)
			case PacketTypeReport:
				s.handler.OnReport(packet, address)
			}
		}
	}
}

//
// reply
// @Description:
// @receiver s
// @param reply
// @param t
// @param uuid
// @param conn
//
func (s *AuthServerV2) reply(reply AuthReply, t PacketType, uuid string, conn io.ReadWriteCloser) {
	defer func() {
		if err := recover(); err != nil {
			log.Info("[uuid:", uuid, "] reply error")
		}
	}()
	b, _ := json.Marshal(reply)
	p := TransportPacket{
		Type:    t,
		UUID:    uuid,
		Payload: b,
	}
	_ = p.Send(conn)
}

//
// login
// @Description:
// @param conn
//
func (s *AuthServerV2) login(conn *net.TCPConn, packet *TransportPacket, address string) {
	//检查是否有在线
	s.lock.Lock()
	if c, ok := s.online[address]; ok && c != nil {
		s.reply(AuthReply{
			Ok:      false,
			Error:   "operation failed, current address is already in use by other user",
			Message: "登录失败，当前地址已有其他用户登录",
		}, PacketTypeLogin, packet.UUID, conn)
		return
	}
	s.lock.Unlock()
	//TODO 验证用户
	cfg := &config.Config{}
	err := json.Unmarshal(packet.Payload, cfg)
	if err != nil {
		s.reply(AuthReply{
			Ok:      false,
			Error:   "read config failed",
			Message: "读取配置失败",
		}, PacketTypeLogin, packet.UUID, conn)
		return
	}
	s.lock.Lock()
	s.online[address] = &Connection{
		UUID:   packet.UUID,
		Config: *cfg,
		Conn:   conn,
	}
	s.lock.Unlock()
	log.Info("[authentication][user:", cfg.User.Account, "] login success")
	s.reply(AuthReply{
		Ok:      true,
		Error:   "",
		Message: "登录成功",
	}, PacketTypeLogin, packet.UUID, conn)
	s.handler.AfterLogin(packet, address)
}

//
// login
// @Description:
// @param conn
//
func (s *AuthServerV2) logout(conn *net.TCPConn, packet *TransportPacket, address string) {
	//检查是否有在线
	s.lock.Lock()
	if c, ok := s.online[address]; !ok || c == nil {
		s.reply(AuthReply{
			Ok:      false,
			Error:   "user not online",
			Message: "用户不在线",
		}, PacketTypeLogout, packet.UUID, conn)
		return
	} else {
		if c.UUID != packet.UUID {
			s.reply(AuthReply{
				Ok:      false,
				Error:   "user not match",
				Message: "用户信息不匹配",
			}, PacketTypeLogout, packet.UUID, conn)
			return
		}
	}
	s.lock.Unlock()
	//TODO 验证用户
	cfg := &config.Config{}
	err := json.Unmarshal(packet.Payload, cfg)
	if err != nil {
		s.reply(AuthReply{
			Ok:      false,
			Error:   "read config failed",
			Message: "读取配置失败",
		}, PacketTypeLogout, packet.UUID, conn)
		return
	}
	//s.online[address].Disconnect()
	s.clearByAddress(address)
	log.Info("[authentication][user:", cfg.User.Account, "] logout success")
	s.reply(AuthReply{
		Ok:      true,
		Error:   "",
		Message: "登出成功",
	}, PacketTypeLogout, packet.UUID, conn)
	s.handler.AfterLogout(packet, address)
}

//
// clearByAddress
// @Description:
// @receiver s
// @param address
//
func (s *AuthServerV2) clearByAddress(address string) {
	s.lock.Lock()
	delete(s.online, address)
	s.lock.Unlock()
}

//
// GetRemoteAddr
// @Description:
// @param conn
// @return string
//
func GetRemoteAddr(conn net.Conn) string {
	remoteAddr := conn.RemoteAddr().String()
	remoteAddr = remoteAddr[0:strings.Index(remoteAddr, ":")]
	return remoteAddr
}
